using System;
using System.Collections.Generic;
using System.Linq;
using Urs.Core;
using Urs.Data.Domain.Stores;
using Urs.Data.Domain.Common;
using Urs.Data.Domain.Users;
using Urs.Data.Domain.Orders;
using Urs.Data.Domain.Shipping;
using Urs.Data.Domain.Configuration;
using Urs.Services.Stores;
using Urs.Services.Common;
using Urs.Services.Payments;
using Urs.Services.Shipping;


namespace Urs.Services.Orders
{
    public partial class OrderTotalCalculationService : IOrderTotalCalculationService
    {
        #region Fields

        private readonly IWorkContext _workContext;
        private readonly IPriceCalculationService _priceCalculationService;
        private readonly IShippingService _shippingService;
        private readonly IPaymentService _paymentService;
        private readonly RewardPointsSettings _rewardPointsSettings;
        private readonly ShippingSettings _shippingSettings;
        private readonly ShoppingCartSettings _shoppingCartSettings;
        private readonly StoreSettings _storeSettings;
        #endregion

        #region Ctor

        public OrderTotalCalculationService(IWorkContext workContext,
            IPriceCalculationService priceCalculationService,
            IShippingService shippingService,
            IPaymentService paymentService,
            RewardPointsSettings rewardPointsSettings,
            ShippingSettings shippingSettings,
            ShoppingCartSettings shoppingCartSettings,
            StoreSettings storeSettings)
        {
            this._workContext = workContext;
            this._priceCalculationService = priceCalculationService;
            this._shippingService = shippingService;
            this._paymentService = paymentService;
            this._rewardPointsSettings = rewardPointsSettings;
            this._shippingSettings = shippingSettings;
            this._shoppingCartSettings = shoppingCartSettings;
            this._storeSettings = storeSettings;
        }

        #endregion

        #region Methods

        public virtual decimal GetShoppingCartSubTotal(IList<ShoppingCartItem> cart)
        {
            var subTotal = decimal.Zero;

            if (cart.Count == 0)
                return subTotal;

            foreach (var shoppingCartItem in cart)
            {
                decimal sciSubTotal = _priceCalculationService.GetSubTotal(shoppingCartItem);
                subTotal += sciSubTotal;
            }

            if (subTotal < decimal.Zero)
                subTotal = decimal.Zero;

            if (_shoppingCartSettings.RoundPricesDuringCalculation)
                subTotal = Math.Round(subTotal, 2);

            return subTotal;
        }


        public virtual decimal GetShoppingCartAdditionalShippingCharge(IList<ShoppingCartItem> cart)
        {
            decimal additionalShippingCharge = decimal.Zero;

            bool isFreeShipping = IsFreeShipping(cart);
            if (isFreeShipping)
                return decimal.Zero;

            foreach (var sci in cart)
                if (sci.IsShipEnabled && !sci.IsFreeShipping)
                    additionalShippingCharge += sci.AdditionalShippingCharge;

            return additionalShippingCharge;
        }

        public virtual bool IsFreeShipping(IList<ShoppingCartItem> cart)
        {
            bool shoppingCartRequiresShipping = cart.RequiresShipping();
            if (!shoppingCartRequiresShipping)
                return true;
            bool allItemsAreFreeShipping = true;
            foreach (var sc in cart)
            {
                if (sc.IsShipEnabled && !sc.IsFreeShipping)
                {
                    allItemsAreFreeShipping = false;
                    break;
                }
            }
            if (allItemsAreFreeShipping)
                return true;
            if (_shippingSettings.FreeShippingOverXEnabled)
            {
                decimal subTotal = GetShoppingCartSubTotal(cart);

                if (subTotal > _shippingSettings.FreeShippingOverXValue)
                    return true;
            }
            return false;
        }

        public virtual decimal AdjustShippingRate(decimal shippingRate,
            IList<ShoppingCartItem> cart)
        {
            if (IsFreeShipping(cart))
                return decimal.Zero;

            decimal shippingFee = GetShoppingCartAdditionalShippingCharge(cart);
            var adjustedRate = shippingRate + shippingFee;

            if (adjustedRate < decimal.Zero)
                adjustedRate = decimal.Zero;

            if (_shoppingCartSettings.RoundPricesDuringCalculation)
                adjustedRate = Math.Round(adjustedRate, 2);

            return adjustedRate;
        }

        public virtual decimal GetShoppingCartShippingTotal(IList<ShoppingCartItem> cart, User user)
        {
            decimal shippingTotal = decimal.Zero;

            bool isFreeShipping = IsFreeShipping(cart);
            if (isFreeShipping)
                return shippingTotal;

            Address shippingAddress = user.ShippingAddress;
            var shippingRateComputationMethods = _shippingService.LoadActiveShippingRateComputationMethods();
            if (shippingRateComputationMethods == null || shippingRateComputationMethods.Count == 0)
                shippingTotal = decimal.Zero;

            if (shippingRateComputationMethods.Count == 1)
            {
                var getShippingOptionRequest = _shippingService.CreateShippingOptionRequest(cart, shippingAddress);

                var shippingRateComputationMethod = shippingRateComputationMethods[0];
                decimal? fixedRate = shippingRateComputationMethod.GetFixedRate(getShippingOptionRequest) ?? default(decimal);
                if (fixedRate.HasValue)
                {
                    shippingTotal = AdjustShippingRate(fixedRate.Value, cart);
                }
            }

            if (_shoppingCartSettings.RoundPricesDuringCalculation)
                shippingTotal = Math.Round(shippingTotal < decimal.Zero ? decimal.Zero : shippingTotal, 2);

            return shippingTotal;
        }

        public virtual decimal GetShoppingCartTotal(IList<ShoppingCartItem> cart, User user = null)
        {
            decimal subtotal = GetShoppingCartSubTotal(cart);
            decimal shippingTotal = GetShoppingCartShippingTotal(cart, user);

            decimal orderTotal = subtotal + shippingTotal;

            if (_shoppingCartSettings.RoundPricesDuringCalculation)
                orderTotal = Math.Round(orderTotal, 2);

            return orderTotal;
        }
        public virtual decimal RedeemedPointsAmount(User user, decimal orderTotal, out int points)
        {
            points = default(int);
            if (user != null && _rewardPointsSettings.Enabled)
            {
                points = user.GetRewardPointsBalance();
                decimal pointsBalanceAmount = ConvertRewardPointsToAmount(points);
                if (orderTotal > decimal.Zero)
                {
                    if (orderTotal > pointsBalanceAmount)
                    {
                        return pointsBalanceAmount;
                    }
                    else
                    {
                        points = ConvertAmountToRewardPoints(orderTotal);
                        return orderTotal;

                    }
                }
            }
            return decimal.Zero;
        }

        public virtual decimal ConvertRewardPointsToAmount(int rewardPoints)
        {
            decimal result = decimal.Zero;
            if (rewardPoints <= 0)
                return decimal.Zero;

            result = rewardPoints * _rewardPointsSettings.ExchangeRate;
            if (_shoppingCartSettings.RoundPricesDuringCalculation)
                result = Math.Round(result, 2);
            return result;
        }

        public virtual int ConvertAmountToRewardPoints(decimal amount)
        {
            int result = 0;
            if (amount <= 0)
                return 0;

            if (_rewardPointsSettings.ExchangeRate > 0)
                result = (int)Math.Ceiling(amount / _rewardPointsSettings.ExchangeRate);
            return result;
        }

        #endregion
    }
}
